#include "sp.h"
#include "common.h"

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define int32u unsigned int
#define SERVERS_GROUP "G_SERVERS"

#define CHATROOM_INFO_DIR "/tmp/para/ChatRoomInfo/"
#define SERVER_INFO_DIR "/tmp/para/ServersInfo/"

#define TRUE 1
#define FALSE 0

#define INFINITE 1000000 /* Some large number */

static	char	User[80] = {'s', 'e', 'r', 'v', 'e', 'r', '\0'};
static  char    Spread_name[80] = "10900@localhost";

static  char            Private_group[MAX_GROUP_NAME];
static  mailbox         Mbox;
static	int             Num_sent;
static	unsigned int	Previous_len;

static  int     To_exit = 0;

static char myServerGroupName[50];
static char myServerClientAllGroupName[50];
static int localId = -1;
static int globalId = -1;
static int myServerId;

#define MAX_MESSLEN     102400
#define MAX_VSSETS      10
#define MAX_MEMBERS     100


static	void	Read_message();
static  void	Bye();

/*************Chat Room Users Data Structures Begin************/
struct UserNode {
    char                    clientId[MAX_CLIENT_ID_LEN];
    char                    userName[MAX_USERNAME_LEN];
    struct UserNode         *next;
};
typedef struct UserNode UserNode;
typedef struct UserNode* UserNodePtr;


struct ChatRoomUserInfoNode {
    char                            chatRoom[MAX_CHATROOMID_LEN];
    UserNodePtr                     listOfUsersHead;
    struct ChatRoomUserInfoNode     *next;
};
typedef struct ChatRoomUserInfoNode ChatRoomUserInfoNode;
typedef struct ChatRoomUserInfoNode* ChatRoomUserInfoNodePtr;

/* Array of Chat Room Lists - Chat Rooms and their users at each Server. */
static ChatRoomUserInfoNodePtr chatRoomUserInfoHead[NUM_SERVERS];


/********* TODO: Not used for now. Meant to be used to handle client crash cases. *******/
struct ClientNode {
    char                    clientId[MAX_CLIENT_ID_LEN];
    char                    userName[MAX_USERNAME_LEN]; /* Not used for now */
    char                    chatRoom[MAX_CHATROOMID_LEN];
    struct ClientNode       *next;
};
typedef struct ClientNode ClientNode;

static ClientNode *clientInfoHeadPtr;
/********* Not used for now End *******/

ChatRoomUserInfoNodePtr getNewChatRoomNode(char *chatRoom) {
    ChatRoomUserInfoNodePtr retPtr;
    retPtr = (ChatRoomUserInfoNodePtr)malloc(sizeof(ChatRoomUserInfoNode));
    strncpy(retPtr->chatRoom, chatRoom, MAX_CHATROOMID_LEN);
    retPtr->next = NULL;
    retPtr->listOfUsersHead = NULL;
    return retPtr;
}

UserNodePtr getNewUserNode(
                char *clientId, 
                char *userName ) {
    UserNodePtr retPtr;
    retPtr = (UserNodePtr)malloc(sizeof(UserNode));
    strncpy(retPtr->clientId, clientId, MAX_CLIENT_ID_LEN);
    strncpy(retPtr->userName, userName, MAX_USERNAME_LEN);
    retPtr->next = NULL;
    return retPtr;
}

void freeUserNode(UserNodePtr userNodePtr) {
    free(userNodePtr);
}

ChatRoomUserInfoNodePtr findChatRoomUserInfoNode(char *chatRoom, int serverId);

ChatRoomUserInfoNodePtr addUserToChatRoom( int serverId,
                char *chatRoom,
                char *userName,
                char *clientId,
                ChatRoomUserInfoNodePtr knownPtr) {

    ChatRoomUserInfoNodePtr     chatRoomUserInfoPtr;
    ChatRoomUserInfoNodePtr     prevChatRoomUserInfoPtr;
    UserNodePtr                 userNodePtr;

    int i;
    ChatRoomUserInfoNodePtr         crUserInfoNodePtr;
    UserNodePtr                     tempUserNodePtr;

/*
    printf("Inside addUserToChatRoom\n");
*/

    if(knownPtr == NULL) {
        /* Find the Chat Room Node */
        prevChatRoomUserInfoPtr = chatRoomUserInfoPtr = chatRoomUserInfoHead[serverId];
        while(chatRoomUserInfoPtr != NULL) {
            if(strncmp(chatRoomUserInfoPtr->chatRoom, chatRoom, MAX_CHATROOMID_LEN) == 0) {
                break;
            }
            prevChatRoomUserInfoPtr = chatRoomUserInfoPtr;
            chatRoomUserInfoPtr = chatRoomUserInfoPtr->next;
        }

        /* If chat room does not exist, create one */
        if(chatRoomUserInfoPtr == NULL) {
            chatRoomUserInfoPtr = getNewChatRoomNode(chatRoom);

            if(chatRoomUserInfoHead[serverId] == NULL) {
                /* This is the first chat room for this Server. */
                chatRoomUserInfoHead[serverId] = chatRoomUserInfoPtr;
            } else {
                /* Add this chat room to the end of the chat room linked list. */
                prevChatRoomUserInfoPtr->next = chatRoomUserInfoPtr;
            }
        }
    } else {
        /* use the pointer passed */
        chatRoomUserInfoPtr = knownPtr;
    }

    /* Add user to the chat room */
    userNodePtr = getNewUserNode(clientId, userName);

/*
    printf("Server Id: %d\n", serverId);
*/
/*
    printf("Adding user[%s] to ChatRoom [%s]\n", userNodePtr->userName, chatRoomUserInfoPtr->chatRoom);
*/
/*
    printf("Before address=[%x]\n", chatRoomUserInfoPtr->listOfUsersHead);
*/

    /* Add this to the beginning of the User's list in the given ChatRoom */
    userNodePtr->next = chatRoomUserInfoPtr->listOfUsersHead;
    chatRoomUserInfoPtr->listOfUsersHead = userNodePtr;
    printf("***********User [%s] was added to cr [%s] at server [%d]\n", userNodePtr->userName, chatRoomUserInfoPtr->chatRoom, serverId);
/*
    printf("After address=[%x]\n", chatRoomUserInfoPtr->listOfUsersHead);
*/


    return chatRoomUserInfoPtr;
}

/* Does not error out if Invalid chatroom or user is provided - Instead, it silently returns. */
void removeUserFromChatRoom(int serverId,
                char *chatRoom,
                char *userName,
                char *clientId) {
    ChatRoomUserInfoNodePtr     chatRoomUserInfoPtr;
    UserNodePtr         prevUserNodePtr = NULL; /* IMP */
    UserNodePtr         userNodePtr;

    /* Find the Chat Room Node */
    chatRoomUserInfoPtr = chatRoomUserInfoHead[serverId];
    while(chatRoomUserInfoPtr != NULL) {
        if(strncmp(chatRoomUserInfoPtr->chatRoom, chatRoom, MAX_CHATROOMID_LEN) == 0) {
            break;
        }
        chatRoomUserInfoPtr = chatRoomUserInfoPtr->next;
    }

    /* Find the user in the Chat Room and remove him/her from the same. */
    if(chatRoomUserInfoPtr != NULL) {
        /* Find the user in the Chat Room */
        userNodePtr = chatRoomUserInfoPtr->listOfUsersHead;
        while(userNodePtr != NULL) {
            if( strncmp(userNodePtr->userName, userName, MAX_USERNAME_LEN) == 0
                    && strncmp(userNodePtr->clientId, clientId, MAX_CLIENT_ID_LEN) == 0) {
                break;
            }
            prevUserNodePtr = userNodePtr;
            userNodePtr = userNodePtr->next;
        }

        /* Remove this user from the chat room */
        if(userNodePtr != NULL) {
            if(prevUserNodePtr == NULL) {
                /* First node in the Linked list */
                chatRoomUserInfoPtr->listOfUsersHead = NULL;
            } else {
                prevUserNodePtr->next = userNodePtr->next;
            }
            freeUserNode(userNodePtr);
        }

    }

}

/* This will be used when a client arbitrarily quits */
void removeClient(char *clientId, char *retUserName, char *retChatRoomName) {
    /* TODO */
}

void removeAllUsers(int serverId) {
    ChatRoomUserInfoNodePtr chatRoomUserInfoPtr, crNodeToBeFreed;
    UserNodePtr userNodePtr, userNodeToBeFreed;

    crNodeToBeFreed = chatRoomUserInfoPtr = chatRoomUserInfoHead[serverId];
    while (chatRoomUserInfoPtr != NULL) {
        /* Remove all users in that chat room */
        userNodeToBeFreed = userNodePtr = chatRoomUserInfoPtr->listOfUsersHead;
        while(userNodePtr != NULL) {
            userNodePtr = userNodePtr->next;
            free(userNodeToBeFreed);
            userNodeToBeFreed = userNodePtr;
        }
        /* Free the chat room node */
        chatRoomUserInfoPtr = chatRoomUserInfoPtr->next;
        free(crNodeToBeFreed);
        crNodeToBeFreed = chatRoomUserInfoPtr;
    }
    chatRoomUserInfoHead[serverId] = NULL;
}
/*************Chat Room Users Data Structures End************/


/*************BEGIN Cache Message Data Structures ************/

struct MsgNode; /* Forward Declare */

/* List of Like and Unlike messages stored at each chat message. */
struct LikeUnlikeMsgPtrNode {
    struct MsgNode*                 msgNodePtr; /* Will always point to a MsgNode corresponding to like or unlike */
    struct LikeUnlikeMsgPtrNode     *next;
};
typedef struct LikeUnlikeMsgPtrNode LikeUnlikeMsgPtrNode;
typedef struct LikeUnlikeMsgPtrNode* LikeUnlikeMsgPtrNodePtr;

/* List of Messages generated by each server. The List is in descending order(according to their LTS). */
typedef struct ServerMsg MsgDetails; /* This has all the message that is needed for the server to send to other servers when requested */
struct MsgNode {
    MsgDetails                      msgDetails;
    struct LikeUnlikeMsgPtrNode*    likeUnlikeMsgPtrsHead; /* Not valid for Like and unlike type of messages */
    struct MsgNode*                 next;
    struct MsgNode*                 prev;
};
typedef struct MsgNode MsgNode;
typedef struct MsgNode* MsgNodePtr;

/* Stores the Head pointer to each server's list of chat messages */
MsgNodePtr msgsHead[NUM_SERVERS];

/* List to Store Users who like or unlike a message*/
struct TempLikeUnlikeUserNode {
    char                            userName[MAX_USERNAME_LEN];
    MsgNodePtr                      latestLikeUnlikeMsgNodePtr;
    struct TempLikeUnlikeUserNode   *next;
};
typedef struct TempLikeUnlikeUserNode TempLikeUnlikeUserNode;
typedef struct TempLikeUnlikeUserNode* TempLikeUnlikeUserNodePtr;



/* List of messages in a chatroom */
struct MsgPtrNode {
    MsgNodePtr          msgNodePtr;
    struct MsgPtrNode   *next;
    struct MsgPtrNode   *prev;
};
typedef struct MsgPtrNode MsgPtrNode;
typedef struct MsgPtrNode* MsgPtrNodePtr;

/* List of Chat rooms. This also points to the head of the list messages in this chat room. */
struct ChatRoomNode {
    char                    chatRoom[MAX_CHATROOMID_LEN];
    MsgPtrNodePtr           msgPtrNodeHead;
    MsgPtrNodePtr           msgPtrNodeTail; /* Needed when all messages in the chat room need to be restored */
    struct ChatRoomNode     *next;
};
typedef struct ChatRoomNode ChatRoomNode;
typedef struct ChatRoomNode* ChatRoomNodePtr;

/* Head of the Linked List of chat rooms */
ChatRoomNodePtr    chatRoomsHead;


MsgNodePtr addMsg(MsgDetails *msgDetails) {
    /* Just add the message to the beginning of the Messages list */

    MsgNodePtr msgNodePtr;

    /* Prepare a new node */
    msgNodePtr = (MsgNodePtr)malloc(sizeof(MsgNode));
    memcpy(&(msgNodePtr->msgDetails), msgDetails, sizeof(MsgDetails));
    msgNodePtr->likeUnlikeMsgPtrsHead = NULL;
    
    /* Add it at the head of the messages list */
    msgNodePtr->next = msgsHead[msgDetails->lts.serverId]; /* initially this will be NULL */
    msgNodePtr->prev = NULL;
    if(msgsHead[msgDetails->lts.serverId] != NULL) {
        msgsHead[msgDetails->lts.serverId]->prev = msgNodePtr;
    }
    msgsHead[msgDetails->lts.serverId] = msgNodePtr;

    /* Return the pointer of the node that was just created */
    return msgNodePtr;
}

/* returns if lts1 is larger than lts2 */
int isLarge(LTS *lts1, LTS *lts2) {
   if(lts1->globalId == lts2->globalId) {
        return (lts1->serverId > lts2->serverId);
    } else {
        return (lts1->globalId > lts2->globalId);
    }
}

/* returns if lts1 is larger than lts2 */
int isSame(LTS *lts1, LTS *lts2) {
   return ((lts1->globalId == lts2->globalId)
           && (lts1->serverId == lts2->serverId));
}

/* Find the position and insert the message pointer at appropriate place in the chat room */
void insertMsgInChatRoom(MsgNodePtr msgPtrToInsert) {
    MsgPtrNodePtr   newMsgPtrNodePtr;
    MsgPtrNodePtr   tempMsgPtrNodePtr;
    ChatRoomNode    *chatRoomNodePtr;
    ChatRoomNode    *prevChatRoomNodePtr;

    /* Prepare the new node */
    newMsgPtrNodePtr = (MsgPtrNodePtr)malloc(sizeof(MsgPtrNode));
    newMsgPtrNodePtr->msgNodePtr = msgPtrToInsert;
    newMsgPtrNodePtr->next = NULL;
    newMsgPtrNodePtr->prev = NULL;

    /* Find the Chat Room. */
    prevChatRoomNodePtr = chatRoomNodePtr = chatRoomsHead;
    while(chatRoomNodePtr != NULL) {
        if(strncmp(chatRoomNodePtr->chatRoom, msgPtrToInsert->msgDetails.userMsg.chatRoom, MAX_CHATROOMID_LEN) == 0) {
            break;
        }
        prevChatRoomNodePtr = chatRoomNodePtr;
        chatRoomNodePtr = chatRoomNodePtr->next;
    }
    /* If chat room not found. Create one and add it to the chat room list. */
    if(chatRoomNodePtr == NULL) {
/*
        printf("insertMsgInChatRoom: Chat room not found. Creating one.\n");
*/
        chatRoomNodePtr = (ChatRoomNodePtr)malloc(sizeof(ChatRoomNode));
        strncpy(chatRoomNodePtr->chatRoom, msgPtrToInsert->msgDetails.userMsg.chatRoom, MAX_CHATROOMID_LEN);
        chatRoomNodePtr->msgPtrNodeHead = chatRoomNodePtr->msgPtrNodeTail = NULL;
        chatRoomNodePtr->next = NULL;
        if(chatRoomsHead == NULL) {
            /* If this was the first chat room that is being created, intialize the chat rooms head to this */
            chatRoomsHead = chatRoomNodePtr;
/*
            printf("insertMsgInChatRoom: Assigning first node in the chat room [%s]\n", chatRoomNodePtr->chatRoom);
*/

        } else {
            /* If not, point the last one this chat room node */
            prevChatRoomNodePtr->next = chatRoomNodePtr;
        }
    }

    /* If there are no messages in the chatroom yet, insert in the beginning */
    if(chatRoomNodePtr->msgPtrNodeHead == NULL) {
        chatRoomNodePtr->msgPtrNodeHead = newMsgPtrNodePtr;
        chatRoomNodePtr->msgPtrNodeTail = newMsgPtrNodePtr;
    } else {
        /* Search and insert the node at appropriate place. Start the search from beginning. 
         * TODO: May be optimize?? May be not needed because the messages are in descending order and it wouldn't take long to find appr. position to insert. */
        tempMsgPtrNodePtr = chatRoomNodePtr->msgPtrNodeHead;
        while( tempMsgPtrNodePtr != NULL
                && !(isLarge( &(msgPtrToInsert->msgDetails.lts), &(tempMsgPtrNodePtr->msgNodePtr->msgDetails.lts) ) == TRUE) ) {
            tempMsgPtrNodePtr = tempMsgPtrNodePtr->next;
/*
            printf("insertMsgInChatRoom: looping...\n"); 
*/
        }
        if(tempMsgPtrNodePtr != NULL) {
            /* There was a message which had LTS lesser than the message being inserted.
             * Now tempMsgPtrNodePtr points to the message which has LTS lesser than the new message being inserted. 
             * So, insert the message above tempMsgPtrNodePtr */

            newMsgPtrNodePtr->next = tempMsgPtrNodePtr;
            newMsgPtrNodePtr->prev = tempMsgPtrNodePtr->prev;

            if(tempMsgPtrNodePtr->prev != NULL) {
                tempMsgPtrNodePtr->prev->next = newMsgPtrNodePtr;
            } else {
                /* It means that tempMsgPtrNodePtr was the first node in the list. */
                chatRoomNodePtr->msgPtrNodeHead = newMsgPtrNodePtr;
            }
            tempMsgPtrNodePtr->prev = newMsgPtrNodePtr;
        } else {
            /* There was no message which had LTS lesser than the message being added. So add this message to the end. */
            chatRoomNodePtr->msgPtrNodeTail->next = newMsgPtrNodePtr;
            newMsgPtrNodePtr->prev = chatRoomNodePtr->msgPtrNodeTail;
        }
    }
}


/*************END Cache Message Data Structures ************/

/*************BEGIN Zombie Messages Data Structures ************/

/* List of Zombie like and unlike messages. This list is sorted in ascending order of LTS of CHAT MESSAGES(and not the LTS of LIKE/UNLIKE messages) */
struct ZombieLikeUnlikeMsgPtrNode {
    MsgNodePtr                          zombieMsgNodePtr;
    struct ZombieLikeUnlikeMsgPtrNode*  next;
};
typedef struct ZombieLikeUnlikeMsgPtrNode ZombieLikeUnlikeMsgPtrNode;
typedef struct ZombieLikeUnlikeMsgPtrNode* ZombieLikeUnlikeMsgPtrNodePtr;

/* Head of zombie like and unlike message pointers */
ZombieLikeUnlikeMsgPtrNodePtr zombieMsgPtrsHead;

void addZombieLikeUnlikeMessage(MsgNodePtr zombieMsgNodePtr) {
    ZombieLikeUnlikeMsgPtrNodePtr curZombieMsgPtrNodePtr, prevZombieMsgPtrNodePtr, newZombieMsgPtrNodePtr;

    /* Create a new Zombie Like/Unlike Msg Pointer Node */
    newZombieMsgPtrNodePtr = (ZombieLikeUnlikeMsgPtrNodePtr)malloc(sizeof(ZombieLikeUnlikeMsgPtrNode));
    newZombieMsgPtrNodePtr->zombieMsgNodePtr = zombieMsgNodePtr;
    newZombieMsgPtrNodePtr->next = NULL;

    /* Find and insert this at appropriate location in the sorted(ascending) zombie message node ptrs list */
    if(zombieMsgPtrsHead == NULL) {
        /* No nodes currently present. So make the new node it as head node */
        zombieMsgPtrsHead = newZombieMsgPtrNodePtr;
    } else {
        /* find its position and insert */
        prevZombieMsgPtrNodePtr = NULL;
        curZombieMsgPtrNodePtr = zombieMsgPtrsHead;
        while (curZombieMsgPtrNodePtr != NULL
                && isLarge( (LTS*)(zombieMsgNodePtr->msgDetails.userMsg.msg),
                                (LTS*)(curZombieMsgPtrNodePtr->zombieMsgNodePtr->msgDetails.userMsg.msg) )) {
            prevZombieMsgPtrNodePtr = curZombieMsgPtrNodePtr;
            curZombieMsgPtrNodePtr = curZombieMsgPtrNodePtr->next;
        }
        if(prevZombieMsgPtrNodePtr == NULL) {
            /* The new node has the smallest LTS. So insert at head. */
            newZombieMsgPtrNodePtr->next = zombieMsgPtrsHead;
            zombieMsgPtrsHead = newZombieMsgPtrNodePtr;
        } else {
            /* Insert it at found location */
            newZombieMsgPtrNodePtr->next = prevZombieMsgPtrNodePtr->next;
            prevZombieMsgPtrNodePtr->next = newZombieMsgPtrNodePtr;

        }
    }
}

MsgNodePtr findAndDeleteZombieMsgPtrNode(LTS *chatMsgLts) {
    ZombieLikeUnlikeMsgPtrNodePtr curZombieMsgPtrNodePtr, prevZombieMsgPtrNodePtr;
    MsgNodePtr retMsgNodePtr = NULL;

    prevZombieMsgPtrNodePtr = NULL;
    curZombieMsgPtrNodePtr = zombieMsgPtrsHead;
    while (curZombieMsgPtrNodePtr != NULL
            && isLarge( chatMsgLts, (LTS*)(&(curZombieMsgPtrNodePtr->zombieMsgNodePtr->msgDetails.userMsg.msg)) )) {
        prevZombieMsgPtrNodePtr = curZombieMsgPtrNodePtr;
        curZombieMsgPtrNodePtr = curZombieMsgPtrNodePtr->next;
    }
    if(curZombieMsgPtrNodePtr != NULL && isSame( (LTS*)(curZombieMsgPtrNodePtr->zombieMsgNodePtr->msgDetails.userMsg.msg),
                          chatMsgLts) ) {
        retMsgNodePtr = curZombieMsgPtrNodePtr->zombieMsgNodePtr;
        /* Remove this node */
        if(curZombieMsgPtrNodePtr == zombieMsgPtrsHead) {
            /* If this is the head node */
            zombieMsgPtrsHead = curZombieMsgPtrNodePtr->next;
        } else {
            prevZombieMsgPtrNodePtr->next = curZombieMsgPtrNodePtr->next;
        }
        free(curZombieMsgPtrNodePtr);
    }
    return retMsgNodePtr;
}

/*************END Zombie Messages Data Structures ************/


/************* Begin Construct Message from Cache ************/

/* Find the chatRoomUserInfo Node */
ChatRoomUserInfoNodePtr findChatRoomUserInfoNode(char *chatRoom, int serverId) {
    ChatRoomUserInfoNodePtr         tempCrUserInfoNodePtr;
    tempCrUserInfoNodePtr = chatRoomUserInfoHead[serverId];
    /* Find the chatRoom Node */
    while (tempCrUserInfoNodePtr != NULL) {
        if(strncmp(tempCrUserInfoNodePtr->chatRoom, chatRoom, MAX_CHATROOMID_LEN) == 0) {
            break;
        }
        tempCrUserInfoNodePtr = tempCrUserInfoNodePtr->next;
    }
    return tempCrUserInfoNodePtr;
}

/* Find the chatRoom Node */
ChatRoomNodePtr findChatRoomNode(char *chatRoom) {
    ChatRoomNodePtr                 tempChatRoomPtr;
    tempChatRoomPtr = chatRoomsHead;
    while (tempChatRoomPtr != NULL) {
        if(strncmp(tempChatRoomPtr->chatRoom, chatRoom, MAX_CHATROOMID_LEN) == 0) {
            break;
        }
        tempChatRoomPtr = tempChatRoomPtr->next;
    }
    return tempChatRoomPtr;
}

/* Prepares Message from Server to Client.
 * Sent as a response when -
 * i)  a user joins a chatroom in this server.
 * ii) User requests to see all msgs. */
void constructClientMsg(char *chatRoom, char *serverToClientMsgBuf, unsigned int returnAll) {

    int i;
    ClientMsgHdr*                   clientMsgHdrPtr = NULL;
    ChatRoomUserInfoNodePtr         crUserInfoNodePtr;
    UserNodePtr                     tempUserNodePtr;
    char*                           data;
    ChatRoomNodePtr                 tempChatRoomPtr;
    MsgPtrNodePtr                   msgPtrNodePtr;
    int                             funNum = 0;
    ChatMsgInfo*                    tempChatMsgInfo;

    clientMsgHdrPtr = (ClientMsgHdr*)serverToClientMsgBuf;
    clientMsgHdrPtr->numOfChatMsgs = 0;
    clientMsgHdrPtr->numUsers = 0;

    /* Message structure is as follows
     * <NumOfUsers><NumOfChatMsgs>
     * <List of <UserName>>
     * <List of <ChatMsgInfo> in descending order>
     */

    data = serverToClientMsgBuf + sizeof(ClientMsgHdr);

/*
    printf("Populate list of users\n");
*/
    /*** Populate list of users in this chatRoom at all Servers ***/
    for(i=0; i < NUM_SERVERS; i++) {
        crUserInfoNodePtr = findChatRoomUserInfoNode(chatRoom, i);
        /* Add all the users in this chat room to the message that will be sent back to the client */
        if(crUserInfoNodePtr != NULL) {
            tempUserNodePtr = crUserInfoNodePtr->listOfUsersHead;
            while (tempUserNodePtr != NULL) {
                (clientMsgHdrPtr->numUsers)++;
                printf("Adding User [%s] (Info from server %d) \n", tempUserNodePtr->userName, i);
                memcpy(data, tempUserNodePtr->userName, MAX_USERNAME_LEN); /* Explicitly copy the entire username memory because the length is not sent to keep things simple. */
                data = data + MAX_USERNAME_LEN;
                tempUserNodePtr = tempUserNodePtr->next;
            }
        }
    }

/*
    printf("Populating list of messages \n");
*/
    /*** Populate list of messages ***/
    tempChatRoomPtr = findChatRoomNode(chatRoom);
    if(tempChatRoomPtr != NULL) { /* This check is needed because there may be no messages in this chat room */
/*
        printf("Found chat room. Traversing through messages\n");
*/
        /* Traverse through the required number of messages */
        msgPtrNodePtr = tempChatRoomPtr->msgPtrNodeHead;
        funNum = DEFAULT_NUM_MSGS;
        while (msgPtrNodePtr != NULL
                && clientMsgHdrPtr->numOfChatMsgs < (funNum+=returnAll)) { /* A fun way to limit the number of messages to return. */
            tempChatMsgInfo = (ChatMsgInfo*)data;
            memcpy(&(tempChatMsgInfo->lts), &(msgPtrNodePtr->msgNodePtr->msgDetails.lts), sizeof(LTS));
            memcpy(tempChatMsgInfo->userName, msgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName, MAX_USERNAME_LEN);
            memcpy(tempChatMsgInfo->msg, msgPtrNodePtr->msgNodePtr->msgDetails.userMsg.msg, MAX_MSG_LEN);
            tempChatMsgInfo->numLikes = determineNumOfLikes(msgPtrNodePtr->msgNodePtr);

            printf("Adding Chat Msg: ");
            printChatMsg(tempChatMsgInfo);

            data = data + sizeof(ChatMsgInfo);
            (clientMsgHdrPtr->numOfChatMsgs)++;
            msgPtrNodePtr = msgPtrNodePtr->next;
        }
    }
/*
    printf("Exiting from constructClientMsg\n");
*/
    
}

/************* End Construct Message from Cache ************/

/* Begin: Partition and healing */

int localIndex[NUM_SERVERS][NUM_SERVERS];

int currentServers[NUM_SERVERS];

int curNumOfServers;

int isCurrentlyHealing = FALSE;

/* List of servers for which I am responsible for sending msgs */
int serversResponsibleFor[NUM_SERVERS];
int numServersResponsibleFor;

/* Last sent localIds of the messages */
int lastSentLocalIdVector[NUM_SERVERS];

void resetAllPartitioningVars(int numServers) {
    int i = 0, j;
    for(i = 0; i < NUM_SERVERS; i++ ) {
        currentServers[i] = FALSE;
        lastSentLocalIdVector[i] = -1;
        serversResponsibleFor[i] = FALSE;
    }
    currentServers[myServerId] = TRUE;
    
    for(i = 0; i < NUM_SERVERS; i++ ) {
        for(j = 0; j < NUM_SERVERS; j++ ) {
            localIndex[i][j] = -1;
        }
    }

    /* Remove all users from all chat rooms in all servers other than mine */
    for(i = 0; i < NUM_SERVERS; i++ ) {
        if(i != myServerId) {
            removeAllUsers(i);
        }
    }

    curNumOfServers = numServers;

    printf("****** setting isCurrentlyHealing TO TRUE ******\n");
    isCurrentlyHealing = TRUE;

    /* Populate my LTS verctor */
    for(i = 0; i < NUM_SERVERS; i++ ) {
        if (msgsHead[i] != NULL) {
            localIndex[myServerId][i] = msgsHead[i]->msgDetails.lts.localId;
        }
    }
}

/* Construct the Partitoning Vector Message */
int constructPartitionMsg(char *serverSendbuf) {
    char                            *data;
    ChatRoomUserInfoNodePtr         crNodePtr = NULL;
    UserNodePtr                     userNodePtr = NULL;
    ServerPartitonMsgHdr            *hdr;

    ChatRoomInfo*                   chatRoomInfoPtr;

    hdr = (ServerPartitonMsgHdr*)serverSendbuf;
    hdr->serverId = myServerId;
    hdr->numChatRooms = 0;
    memcpy(hdr->localIdVector, localIndex[myServerId], NUM_SERVERS*sizeof(int));

    data = serverSendbuf + sizeof(ServerPartitonMsgHdr);

    crNodePtr = chatRoomUserInfoHead[myServerId];
    while(crNodePtr != NULL) {
        chatRoomInfoPtr = (ChatRoomInfo*)data;
        hdr->numChatRooms++;
        memcpy(chatRoomInfoPtr->chatRoom, crNodePtr->chatRoom, MAX_CHATROOMID_LEN);
        chatRoomInfoPtr->numUsers = 0;

        data = data + sizeof(ChatRoomInfo);

        userNodePtr = crNodePtr->listOfUsersHead;
        while(userNodePtr != NULL) {
            memcpy(data, userNodePtr->userName, MAX_USERNAME_LEN);
            data+=MAX_USERNAME_LEN;
            memcpy(data, userNodePtr->clientId, MAX_CLIENT_ID_LEN);
            data+=MAX_CLIENT_ID_LEN;
            (chatRoomInfoPtr->numUsers)++;
            userNodePtr = userNodePtr->next;
        }

        crNodePtr = crNodePtr->next;
    }
    return (data - serverSendbuf);
}

void printPartitionMsg(char *serverSendbuf) {
    char                            *data;
    ChatRoomUserInfoNodePtr         crNodePtr = NULL;
    UserNodePtr                     userNodePtr = NULL;
    ServerPartitonMsgHdr            *hdr;
    int                             i;


    ChatRoomInfo*                   chatRoomInfoPtr;
    int                             chatRoomCtr = 0;
    int                             numUsrsCtr = 0;

    hdr = (ServerPartitonMsgHdr*)serverSendbuf;
    data = serverSendbuf + sizeof(ServerPartitonMsgHdr);

    printf("**************************************************\n");
    printf("Server [%d]\n", hdr->serverId);
    printf("LTS vector [%d] [%d] [%d] [%d] [%d] \n", hdr->localIdVector[0], hdr->localIdVector[1], hdr->localIdVector[2], hdr->localIdVector[3], hdr->localIdVector[4]);
    printf("NumChatRooms=[%d] \n", hdr->numChatRooms);

    while(chatRoomCtr < hdr->numChatRooms) {
        chatRoomInfoPtr = (ChatRoomInfo*)data;
        printf("ChatRoomInfo = [%s] [%d] \n", chatRoomInfoPtr->chatRoom, chatRoomInfoPtr->numUsers);
        data = data + sizeof(ChatRoomInfo);
        numUsrsCtr = 0;
        crNodePtr = NULL;
        while(numUsrsCtr < chatRoomInfoPtr->numUsers) {
            data = data + MAX_USERNAME_LEN + MAX_CLIENT_ID_LEN;
            numUsrsCtr++;
        }
        chatRoomCtr++;
    }
    printf("**************************************************\n");

}

/* Parse the Partitoning Vector Message */
void parsePartitionMsg(char *recvBuf) {
    char                            *data;
    ChatRoomUserInfoNodePtr         crNodePtr = NULL;
    UserNodePtr                     userNodePtr = NULL;
    ServerPartitonMsgHdr            *hdr;

    ChatRoomInfo*                   chatRoomInfoPtr;
    int                             chatRoomCtr = 0;
    int                             numUsrsCtr = 0;

    hdr = (ServerPartitonMsgHdr*)recvBuf;
    data = recvBuf + sizeof(ServerPartitonMsgHdr);

    memcpy(localIndex[hdr->serverId], hdr->localIdVector, NUM_SERVERS*sizeof(int));

    currentServers[hdr->serverId] = TRUE;

    while(chatRoomCtr < hdr->numChatRooms) {
        chatRoomInfoPtr = (ChatRoomInfo*)data;
        data = data + sizeof(ChatRoomInfo);
        numUsrsCtr = 0;
        crNodePtr = NULL;
        while(numUsrsCtr < chatRoomInfoPtr->numUsers) {
            crNodePtr = addUserToChatRoom(hdr->serverId, chatRoomInfoPtr->chatRoom, data, data+MAX_USERNAME_LEN, crNodePtr);
            data = data + MAX_USERNAME_LEN + MAX_CLIENT_ID_LEN;
            numUsrsCtr++;
        }
        chatRoomCtr++;
    }
}

int areAllPartionMsgsRecvd() {
    int i;
    int ret = FALSE;
    int curRecvd = 0;

    for(i = 0; i < NUM_SERVERS; i++) {
        if(currentServers[i] == TRUE) {
            curRecvd++;
        }
    }

    return (curRecvd == curNumOfServers);
}

int getMax(int forServer) {
    int tmp, i;
    tmp = -1;
    for(i = 0; i < NUM_SERVERS; i++ ) {
        if(currentServers[i] == TRUE) {
            if(tmp < localIndex[i][forServer]) {
                tmp = localIndex[i][forServer];
            }
        }
    }
    return tmp;
}

int getMin(int forServer) {
    int tmp, i;
    tmp = INFINITE;
    for(i = 0; i < NUM_SERVERS; i++ ) {
        if(currentServers[i] == TRUE) {
            if(tmp > localIndex[i][forServer]) {
                tmp = localIndex[i][forServer];
            }
        }
    }
    return tmp;
}

/* End: Partition and healing */


void updateGlobalId(int globalIdParam) {
    if(globalIdParam > globalId) {
        globalId = globalIdParam;
    }
}


void constructLTS(LTS* lts){
    lts->globalId = ++globalId;
    lts->localId = ++localId;
    lts->serverId = myServerId;
}

/* TODO This method is not needed. Remove this */
void updateLtsFile(LTS *lts){
    char serverInfoFile[50];
    FILE* fw;
    char            serverIdStr[10];

    sprintf(serverIdStr, "%d\0", lts->serverId);

    strcpy(serverInfoFile, SERVER_INFO_DIR);
    strcat(serverInfoFile, serverIdStr);

    /* Open the source file for writing */
    if ((fw = fopen(serverInfoFile, "w")) == NULL) {
        perror("fopen error");
        Bye();
    }

    /* write to file */
    fwrite(lts, 1, sizeof(UserMsg), fw);
    fclose(fw);
}

void constructUserMsgFilename(int serverId, char *retMsgFilename) {
    sprintf(retMsgFilename, "%sSERVER_%d\0", CHATROOM_INFO_DIR, serverId);
}


void writeAppendMsgToFile(ServerMsg *serverMsg) {
    char                            filename[50];
    char                            gChatRoom[MAX_SPREAD_GROUP_LEN];
    FILE                            *fw;

    /* Update the current LTS */
    updateLtsFile(&(serverMsg->lts)); /* TODO This is not needed */

    /* Create the CHAT File */
    constructUserMsgFilename(serverMsg->lts.serverId, filename);
/*
    printf("Adding message to file [%s] \n", filename);
*/
    if ((fw = fopen(filename, "ab")) == NULL) {
        perror("fopen error");
        Bye();
    }
    fwrite(serverMsg, sizeof(ServerMsg), 1, fw);

    /* LocalIndex~GlobalIndex~ChatRoomName~type~User~Msg */
    /*
    fprintf(fw,"%d~%d~%s~%d~%s~%s\n",
                serverMsg->lts.localId, serverMsg->lts.globalId, serverMsg->userMsg.chatRoom,
                serverMsg->userMsg.type, serverMsg->userMsg.userName, serverMsg->userMsg.msg);
    */


    fclose(fw);
}

void writeLikeUnlikeMessageToFile(ServerMsg *serverMsg) {
    char            filename[50];
    char            gChatRoom[MAX_SPREAD_GROUP_LEN];
    FILE            *fw;
    int             ret;

    /* Update the current LTS */
    updateLtsFile(&(serverMsg->lts)); /* TODO This is not needed */

    /* Create the Like file */
    constructUserMsgFilename(serverMsg->lts.serverId, filename);
    if ((fw = fopen(filename, "ab")) == NULL) {
        perror("fopen error");
        Bye();
    }
    fwrite(serverMsg, sizeof(ServerMsg), 1, fw);

    /* LocalIndex~GlobalIndex~ChatRoomName~type~User~LocalIndexOfChatMsg~GlobalIndexOfChatMsg~ServerIdOfChatMsg */
    /*
    fprintf(fw, "%d~%d~%s"
                "~%d~%s"
                "~%d~%d~%d\n",
                serverMsg->lts.localId, serverMsg->lts.globalId, serverMsg->userMsg.chatRoom,
                serverMsg->userMsg.type, serverMsg->userMsg.userName,
                ((LTS *)(serverMsg->userMsg.msg))->localId, ((LTS *)(serverMsg->userMsg.msg))->globalId, ((LTS *)(serverMsg->userMsg.msg))->serverId );
    */

    fclose(fw);
}

/* inserts the like/unlike message ptr to the chat msg node. If chat message ptr is null, adds it to the zombie list. */
void insertLikeUnlikeMessagePtrNodePtr(MsgNodePtr chatMsgNodePtr, MsgNodePtr likeUnlikeMsgNodePtr) {
    LikeUnlikeMsgPtrNodePtr likeUnlikeMsgPtrNodePtr;
/*
    printf("insertLikeUnlikeMessagePtrNodePtr: Inside\n");
*/
    if(chatMsgNodePtr != NULL) {
        /* Create a node */
        likeUnlikeMsgPtrNodePtr = (LikeUnlikeMsgPtrNodePtr)malloc(sizeof(LikeUnlikeMsgPtrNode));
        likeUnlikeMsgPtrNodePtr->msgNodePtr = likeUnlikeMsgNodePtr;
        likeUnlikeMsgPtrNodePtr->next = NULL;
/*
        printf("insertLikeUnlikeMessagePtrNodePtr: Adding a like message\n");
*/
        /* Directly add(at head) the newly created node to the list of likeUnlikeMsgNodes of the chat message */
        likeUnlikeMsgPtrNodePtr->next = chatMsgNodePtr->likeUnlikeMsgPtrsHead;
/*
        printf("insertLikeUnlikeMessagePtrNodePtr: Adding still a like message\n");
*/
        chatMsgNodePtr->likeUnlikeMsgPtrsHead = likeUnlikeMsgPtrNodePtr;
/*
        printf("insertLikeUnlikeMessagePtrNodePtr: Added a like message\n");
*/
    } else {
        /* The chat message does not exist. So add it to Data structure#3 and eventually it will be consumed */
        printf("insertLikeUnlikeMessagePtrNodePtr: Adding a zombie like message\n");
        addZombieLikeUnlikeMessage(likeUnlikeMsgNodePtr);
    }
/*
    printf("insertLikeUnlikeMessagePtrNodePtr: Exiting\n");
*/
}

void handleUserMsg_Append(ServerMsg *serverMsg) {
    char                            filename[50];
    char                            gChatRoom[MAX_SPREAD_GROUP_LEN];
    FILE                            *fw;
    int                             ret;
    MsgNodePtr                      msgNodePtr;
    MsgNodePtr                      zombieMsgNodePtr;

    /* Add this message in the data structures */
    msgNodePtr = addMsg(serverMsg);
    insertMsgInChatRoom(msgNodePtr);
/*
    printf("Added message to two Linked lists in data structure 2\n");
*/

    /* Check if there are any likes or unlikes in the zombie messages list, related to this chat message */
    while ((zombieMsgNodePtr = findAndDeleteZombieMsgPtrNode(&(serverMsg->lts))) != NULL) {
        insertLikeUnlikeMessagePtrNodePtr(msgNodePtr, zombieMsgNodePtr);
    }
}

/* Find this message in the list of messages corresponding to that Server */
MsgNodePtr findChatMsg(int serverId, int localId) {
    MsgNodePtr msgNodePtr;
/*
    printf("Finding chat message [%d] [%d]\n", serverId, localId);
*/
    msgNodePtr = msgsHead[serverId];
    while(msgNodePtr != NULL) {
/*
        printf("Loop: Finding chat message [%d] \n", msgNodePtr->msgDetails.lts.localId);
*/
        if(msgNodePtr->msgDetails.lts.localId == localId) {
            break;
        }
        msgNodePtr = msgNodePtr->next;
    }
    return msgNodePtr;
}


void handleUserMsg_Like_And_Unlike(ServerMsg *serverMsg, MsgNodePtr chatMsgNodePtr) {
    MsgNodePtr      newMsgNodePtr;
    LikeUnlikeMsgPtrNodePtr likeUnlikeMsgPtrNodePtr;

/*
    printf("handleUserMsg_Like_And_Unlike: Adding message\n");
*/
    /* 1. Add this message in the list of user messages(from a particular server) */
    newMsgNodePtr = addMsg(serverMsg);

/*
    printf("handleUserMsg_Like_And_Unlike: finding chat message if needed. \n");
*/
    /* 2. Add the like/unlike message to the list of list of likeUnlikeMsgNodes of the chat message
     * (only of the chat message is already present. If not, store it as a zombie message). */
    if(chatMsgNodePtr == NULL) {
        /* Find the Chat Message Node */
        chatMsgNodePtr = findChatMsg(((LTS*)(serverMsg->userMsg.msg))->serverId, ((LTS*)(serverMsg->userMsg.msg))->localId);
    }

/*
    printf("handleUserMsg_Like_And_Unlike: inserting insertLikeUnlikeMessagePtrNodePtr. \n");
*/
    insertLikeUnlikeMessagePtrNodePtr(chatMsgNodePtr, newMsgNodePtr);
/*
    printf("handleUserMsg_Like_And_Unlike: Exiting. \n");
*/
}


/* Builds a list of TempLikeUnlikeUserNode nodes corresponding to the chat message. */
TempLikeUnlikeUserNodePtr buildTempLikeUnlikeUsersList(MsgNodePtr chatMsgNodePtr) {
    LikeUnlikeMsgPtrNodePtr     itrLikeUnlikeMsgPtrNodePtr;
    TempLikeUnlikeUserNodePtr   headTempPtr = NULL, tempTempPtr, curTempPtr, prevTempPtr;

/*
    printf("buildTempLikeUnlikeUsersList: Inside \n");
*/
    itrLikeUnlikeMsgPtrNodePtr = chatMsgNodePtr->likeUnlikeMsgPtrsHead;
    while (itrLikeUnlikeMsgPtrNodePtr != NULL) {
        /* Find this user's node in the list of temp nodes */
        curTempPtr = prevTempPtr = headTempPtr;
        while(curTempPtr != NULL) {
            if(strncmp(curTempPtr->userName, itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName, MAX_USERNAME_LEN) == 0) {
/*
                printf("buildTempLikeUnlikeUsersList: Found username[%s] \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/
                break;
            }
            prevTempPtr = curTempPtr;
            curTempPtr = curTempPtr->next;
        }
        if(curTempPtr == NULL) {
            /* If not found, add the Temp.... Linked List */
            /* Prepare the Temp node */
/*
            printf("buildTempLikeUnlikeUsersList: Not found username[%s] \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/
            tempTempPtr = (TempLikeUnlikeUserNodePtr)malloc(sizeof(TempLikeUnlikeUserNode));
            strncpy(tempTempPtr->userName, itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName, MAX_USERNAME_LEN);
            tempTempPtr->latestLikeUnlikeMsgNodePtr = itrLikeUnlikeMsgPtrNodePtr->msgNodePtr;
            tempTempPtr->next = NULL;

            if(headTempPtr == NULL) {
                /* If this was the first node in the Temp.... Linked List */
/*
                printf("buildTempLikeUnlikeUsersList: Adding [%s] to head \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/
                headTempPtr = tempTempPtr;
            } else {
                /* else, append it to the end */
/*
                printf("buildTempLikeUnlikeUsersList: Adding [%s] to end \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/
                prevTempPtr->next = tempTempPtr;
            }
        } else {
            /* If found, check if the LTS of it's likeUnlike message is lesser the one pointed by itrLikeUnlikeMsgPtrNodePtr.
             * If so, replace the pointer. */
/*
            printf("buildTempLikeUnlikeUsersList: Found username[%s]. Checking if its larger. \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/
            if(isLarge(&(itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.lts), &(curTempPtr->latestLikeUnlikeMsgNodePtr->msgDetails.lts))) {
                curTempPtr->latestLikeUnlikeMsgNodePtr = itrLikeUnlikeMsgPtrNodePtr->msgNodePtr;
            }
        }
/*
        printf("buildTempLikeUnlikeUsersList: No luck with username[%s]. Iterating... \n", itrLikeUnlikeMsgPtrNodePtr->msgNodePtr->msgDetails.userMsg.userName);
*/

        itrLikeUnlikeMsgPtrNodePtr = itrLikeUnlikeMsgPtrNodePtr->next;
    }
    return headTempPtr;
}

int determineIfUserLikes(char* userName, TempLikeUnlikeUserNodePtr tempLikeUnlikeUserNodeHead) {
    TempLikeUnlikeUserNodePtr   curTempPtr;
    int                         doesUserLike = FALSE;
    curTempPtr = tempLikeUnlikeUserNodeHead;
/*
    printf("determineIfUserLikes: Inside, checking if [%s] likes\n", userName);
*/
    while (curTempPtr != NULL) {
/*
        printf("determineIfUserLikes: Inside, checking if [%s] likes\n", userName);
*/
        if(strncmp(curTempPtr->userName, userName, MAX_USERNAME_LEN) == 0) {
/*
            printf("determineIfUserLikes: Username [%s] found\n", userName);
*/
            if(curTempPtr->latestLikeUnlikeMsgNodePtr->msgDetails.userMsg.type == USERMSG_LIKE) {
                doesUserLike = TRUE;
            } else {
                doesUserLike = FALSE;
            }
            break;
        }
        curTempPtr = curTempPtr->next;
    }
/*
    printf("determineIfUserLikes: returning [%d]\n", doesUserLike);
*/
    return doesUserLike;
}

void freeTempLikeUnlikeUsersList(TempLikeUnlikeUserNodePtr tempLikeUnlikeUserNodeHead) {
    TempLikeUnlikeUserNodePtr   curTempPtr, nodeToBeFreed;
    
    nodeToBeFreed = curTempPtr = tempLikeUnlikeUserNodeHead;
    while (curTempPtr != NULL) {
        curTempPtr = curTempPtr->next;
        free(nodeToBeFreed);
        nodeToBeFreed = curTempPtr;
    }
}

/* Returns if the message is liked by the user. */
int isLikedByUser(char *userName, MsgNodePtr chatMsgNodePtr) {
    int retIsLiked = FALSE;
    TempLikeUnlikeUserNodePtr tempLikeUnlikeUserNodeHead;

    tempLikeUnlikeUserNodeHead = buildTempLikeUnlikeUsersList(chatMsgNodePtr);
    retIsLiked = determineIfUserLikes(userName, tempLikeUnlikeUserNodeHead);
    freeTempLikeUnlikeUsersList(tempLikeUnlikeUserNodeHead);

    return retIsLiked;
}

/* Returns the number of users who like the given chatMsg */
int determineNumOfLikes(MsgNodePtr chatMsgNodePtr) {
    int numLikes = 0;
    TempLikeUnlikeUserNodePtr tempLikeUnlikeUserNodeHead, curTempPtr;

/*
    printf("determineNumOfLikes: building ...\n");
*/
    tempLikeUnlikeUserNodeHead = buildTempLikeUnlikeUsersList(chatMsgNodePtr);
    curTempPtr = tempLikeUnlikeUserNodeHead;
    
/*
    printf("determineNumOfLikes: determining ...\n");
*/
    while (curTempPtr != NULL) {
/*
        printf("determineNumOfLikes: curTempPtr is NOT null ...\n");
*/
        if(curTempPtr->latestLikeUnlikeMsgNodePtr->msgDetails.userMsg.type == USERMSG_LIKE) {
            numLikes++;
        }
        curTempPtr = curTempPtr->next;
    }

/*
    printf("determineNumOfLikes: freeing ...\n");
*/
    freeTempLikeUnlikeUsersList(tempLikeUnlikeUserNodeHead);

    return numLikes;
}

/* Healing Step 6. Send msgs to all modified CRs(right now for all of them) */
void notifyAllClients(char *serverSendbuf) {
    ChatRoomUserInfoNodePtr     chatRoomUserInfoNodePtr;
    int                         ret;
    char                        gChatRoom[MAX_SPREAD_GROUP_LEN];
    ClientMsgHdr*               tmpClientMsgHdr;
    int                         msgLength;

    /* Notofy to all the chatrooms in which users to my server are connected to. */
    chatRoomUserInfoNodePtr = chatRoomUserInfoHead[myServerId];
    while (chatRoomUserInfoNodePtr != NULL) {
        constructClientMsg(chatRoomUserInfoNodePtr->chatRoom, serverSendbuf, FALSE);
        /* Notify Clients */
        constructChatRoomGroupName(chatRoomUserInfoNodePtr->chatRoom, gChatRoom, myServerId);
        tmpClientMsgHdr = (ClientMsgHdr*)serverSendbuf;
        msgLength = sizeof(ClientMsgHdr) + (tmpClientMsgHdr->numUsers)*MAX_USERNAME_LEN + (tmpClientMsgHdr->numOfChatMsgs)*sizeof(ChatMsgInfo);

        printf("Sending ALL update to client group [%s]...\n", gChatRoom);
        ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT, msgLength, serverSendbuf); /* Send it to group, but as a Client message */
        if (ret < 0) {
            SP_error(ret);
            Bye();
        }
        chatRoomUserInfoNodePtr = chatRoomUserInfoNodePtr->next;
    }
}

/***** Begin: Init Functions *****/

void populateMsg(char* filename, int serverId) {
    ServerMsg serverMsg;
    LTS tempLts;

    FILE *fr;
    int bytesRead = 0;

    if ((fr = fopen(filename, "rb")) == NULL) {
        perror("fopen error");
        printf("Error opening the file. Starting from scratch\n");
        return;
    }

    /*TODO: remove this if needed later: serverMsg.lts.serverId = serverId; */
    printf("\n\n******* Begin Reading messages of Server [%d] *******\n", serverId);
    bytesRead = fread(&serverMsg, 1, sizeof(ServerMsg), fr);
    while( !feof(fr) && bytesRead == sizeof(ServerMsg)) {
        if (serverMsg.userMsg.type == USERMSG_LIKE || serverMsg.userMsg.type == USERMSG_UNLIKE) {
            printServerMsg(&serverMsg);
            handleUserMsg_Like_And_Unlike(&serverMsg, NULL);
        } else {
            printServerMsg(&serverMsg);
            handleUserMsg_Append(&serverMsg);
        }
        bytesRead = fread(&serverMsg, 1, sizeof(ServerMsg), fr);

        /*
        fscanf(fr, "%d %d %s %d %s %s\n", &(serverMsg.lts.localId), &(serverMsg.lts.globalId), serverMsg.userMsg.chatRoom,
                &(serverMsg.userMsg.type), serverMsg.userMsg.userName, serverMsg.userMsg.msg);
        if (serverMsg.userMsg.type == USERMSG_LIKE || serverMsg.userMsg.type == USERMSG_UNLIKE) {
            sscanf(serverMsg.userMsg.msg, "%d~%d~%d", &(tempLts.localId), &(tempLts.globalId), &(tempLts.serverId));
            memcpy(serverMsg.userMsg.msg, &tempLts, sizeof(LTS));
            printf("Handling like/unlike message ... \n");
            printServerMsg(&serverMsg);
            handleUserMsg_Like_And_Unlike(&serverMsg, NULL);
        } else {
            printf("Handling append message ... \n");
            printServerMsg(&serverMsg);
            handleUserMsg_Append(&serverMsg);
        }
        */
    }
    printf("******* End Reading messages of Server [%d] *******\n", serverId);

    fclose(fr);
    if( serverId == myServerId){
        localId = serverMsg.lts.localId;
        globalId = serverMsg.lts.globalId;
    }
}

void init() {
    int i;
    char fileName[50];
    for (i = 0; i < NUM_SERVERS; i++) {
        constructUserMsgFilename(i, fileName);
        populateMsg(fileName, i);
    }
}

/***** End: Init Functions *****/

int main( int argc, char *argv[] )
{
	int	    ret;
        int     mver, miver, pver;
        sp_time test_timeout;

        test_timeout.sec = 5;
        test_timeout.usec = 0;

        if (!SP_version( &mver, &miver, &pver)) {
              printf("main: Illegal variables passed to SP_version()\n");
              Bye();
        }
        printf("Spread library version is %d.%d.%d\n", mver, miver, pver);

        if(argc != 2) {
            printf("Usage: ./server <serverid>");
            return -1;
        }


        strncat(User, argv[1], 1);
        myServerId = atoi(argv[1]);

        /* TODO: Local Index read from file*/
        ret = SP_connect_timeout( Spread_name, User, 0, 1, &Mbox, Private_group, test_timeout );
        if( ret != ACCEPT_SESSION )
        {
                SP_error( ret );
                Bye();
        }
        printf("User: connected to %s with private group %s\n", Spread_name, Private_group );

        init();
        printf("Server[%d] Started with LocalId [%d] GlobalId [%d] ...\n\n\n", myServerId, localId, globalId);

        /* Join the Individual Server Group */
        strcpy(myServerGroupName, SERVER_PRIVATE_GROUP);
        strncat(myServerGroupName, argv[1], 1);
        ret = SP_join( Mbox, myServerGroupName );
        if( ret < 0 ) SP_error( ret );

        /* Join the Servers Group */
        ret = SP_join( Mbox, SERVERS_GROUP );
        if( ret < 0 ) SP_error( ret );

        /* Join the Group where this Server and all clients connected to this server are present. */
        strcpy(myServerClientAllGroupName, SERVER_CLIENT_ALL);
        strncat(myServerClientAllGroupName, argv[1], 1);
        ret = SP_join( Mbox, myServerClientAllGroupName );
        if( ret < 0 ) SP_error( ret );

        
        E_init();

        E_attach_fd( Mbox, READ_FD, Read_message, 0, NULL, HIGH_PRIORITY );

        fflush(stdout);

        E_handle_events();

        return( 0 );
}

/* Tells whether to ignore the server message or not */
int isIgnore(LTS *lts) {
    int isIgnore = FALSE;
    MsgNodePtr msgsNodeHeadPtr = msgsHead[lts->serverId];
    if (msgsNodeHeadPtr != NULL) {
        if(!isLarge(lts, &(msgsNodeHeadPtr->msgDetails.lts) )) {
            isIgnore = TRUE;
        }
    }
    return isIgnore;
}

static	void	Read_message()
{

        static	char	 mess[MAX_MESSLEN];
        static	char     recv_buf[MAX_MESSLEN];
        static	char     serverSendbuf[MAX_MESSLEN];
        char		 sender[MAX_GROUP_NAME];
        char		 target_groups[MAX_MEMBERS][MAX_GROUP_NAME];
        membership_info  memb_info;
        vs_set_info      vssets[MAX_VSSETS];
        unsigned int     my_vsset_index;
        int              num_vs_sets;
        char             members[MAX_MEMBERS][MAX_GROUP_NAME];
        int		 num_groups;
        int		 service_type;
        int16		 mess_type;
        int		 endian_mismatch;
        int		 i,j;
        int		 ret;
        UserMsg          *userMsg;
        ServerMsg        *serverMsg;
        FILE             *fw;
        char             filename[50];
        char             gChatRoom[MAX_SPREAD_GROUP_LEN];
        int              isLikedAlready;
        ClientMsgHdr*    tmpClientMsgHdr;
        int              msgLength;
        MsgNodePtr       chatMsgNodePtr;
        int              isContinue;
        ChatRoomNodePtr  chatRoomNodePtr;
        int              loopCtr;
        char*            data;

        service_type = 0;

	ret = SP_receive( Mbox, &service_type, sender, 100, &num_groups, target_groups, 
		&mess_type, &endian_mismatch, sizeof(recv_buf), recv_buf );
	printf("\n============================\n");
	if( ret < 0 ) 
	{
                if ( (ret == GROUPS_TOO_SHORT) || (ret == BUFFER_TOO_SHORT) ) {
                        service_type = DROP_RECV;
                        printf("\n========Buffers or Groups too Short=======\n");
                        ret = SP_receive( Mbox, &service_type, sender, MAX_MEMBERS, &num_groups, target_groups, 
                                          &mess_type, &endian_mismatch, sizeof(recv_buf), recv_buf );
                }
        }
        if (ret < 0 )
        {
		if( ! To_exit )
		{
			SP_error( ret );
			printf("\n============================\n");
			printf("\nBye.\n");
		}
		exit( 0 );
	}
	if( Is_regular_mess( service_type ) )
	{
		if     ( Is_unreliable_mess( service_type ) ) printf("received UNRELIABLE ");
		else if( Is_reliable_mess(   service_type ) ) printf("received RELIABLE ");
		else if( Is_fifo_mess(       service_type ) ) printf("received FIFO ");
		else if( Is_causal_mess(     service_type ) ) printf("received CAUSAL ");
		else if( Is_agreed_mess(     service_type ) ) printf("received AGREED ");
		else if( Is_safe_mess(       service_type ) ) printf("received SAFE ");
		printf("message from %s, of type %d, (endian %d) to %d groups \n(%d bytes): %s\n",
			sender, mess_type, endian_mismatch, num_groups, ret, recv_buf );

                /**********************  1. From a client  **********************/
                if (strcmp(target_groups[0], myServerGroupName) == 0) {
                    /* Switch case for: Join, Leave, Append, Like, Unlike, History, View */
                    userMsg = (UserMsg*) recv_buf;
                    printf("\nRecieved User Message ... \n");
                    printUserMsg(userMsg);
                    serverMsg = (ServerMsg*) serverSendbuf;
                    memcpy(&(serverMsg->userMsg), userMsg, sizeof (UserMsg));
                    strncpy(serverMsg->clientId, sender, MAX_CLIENT_ID_LEN);
                    serverMsg->lts.serverId = myServerId; /* Important */

                    printf("\nPrepared the following ServerMsg using the recieved UserMsg\n");
                    printServerMsg(serverMsg);


                    switch (userMsg->type) {

                        case USERMSG_JOIN:
                            /* Add user to the list of current users in that chatroom. TODO: Check this - Note that "sender" is used as clientId. */
                            addUserToChatRoom(myServerId, userMsg->chatRoom, userMsg->userName, sender, NULL);

                            /* Notify Servers */
                            printf("\nSending message to Servers group [%s] \n", SERVERS_GROUP);
                            ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER, sizeof(ServerMsg), (char*)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }

                            /* Notify Clients */
                            constructChatRoomGroupName(userMsg->chatRoom, gChatRoom, myServerId);
                            printf("\nSending message to Client group [%s] \n", gChatRoom);
                            printServerMsg(serverMsg);
                            ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char*)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }

                            /* Send to Client: last 25 msgs & list of users in the chat room to the user who just joined this server. */
                            constructClientMsg(userMsg->chatRoom, serverSendbuf, FALSE);

                            tmpClientMsgHdr = (ClientMsgHdr*)serverSendbuf;
                            msgLength = sizeof(ClientMsgHdr) + (tmpClientMsgHdr->numUsers)*MAX_USERNAME_LEN + (tmpClientMsgHdr->numOfChatMsgs)*sizeof(ChatMsgInfo);

                            printf("\nSending message to Client Alone Group [%s] \n", sender);
                            printf("NumUsers=[%d] NumMsgs=[%d]\n", tmpClientMsgHdr->numUsers, tmpClientMsgHdr->numOfChatMsgs);
                            
                            ret = SP_multicast(Mbox, FIFO_MESS, sender, SERVER_TO_CLIENT, msgLength, (char *)serverSendbuf);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }

                            break;
                            
                        case USERMSG_LEAVE:
                            /* Remove this user from the list of current users */
                            removeUserFromChatRoom(myServerId, userMsg->chatRoom, serverMsg->userMsg.userName, sender);

                            /* Notify Servers */
                            printf("\nSending message to Servers group [%s] \n", SERVERS_GROUP);
                            ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER, sizeof(ServerMsg), (char*)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }

                            /* Notify Clients */
                            constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                            printf("\nSending message to Client group [%s] \n", gChatRoom);
                            ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            break;

                        case USERMSG_APPEND:
                            constructLTS(&(serverMsg->lts));

                            writeAppendMsgToFile(serverMsg);

                            handleUserMsg_Append(serverMsg);
                            
                            /* Notify Servers */
                            printf("\nSending message to Servers group [%s] \n", SERVERS_GROUP);
                            ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER, sizeof(ServerMsg), (char *)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }

                            /* Notify Clients */
                            constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                            printf("\nSending message to Client group [%s] \n", gChatRoom);
                            ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            printf("Sending user message to client group [%s]\n", gChatRoom);
                            printServerMsg(serverMsg);

                            break;
                            
                        case USERMSG_LIKE:
                            /* Checks if the user has already liked the message.  */
                            printf("findChatMsg before\n");
                            chatMsgNodePtr = findChatMsg( ((LTS*)(userMsg->msg))->serverId, ((LTS*)(userMsg->msg))->localId );
                            if(chatMsgNodePtr != NULL) {
                                printf("Found\n");
                            }
                            if(strncmp(chatMsgNodePtr->msgDetails.userMsg.userName, userMsg->userName, MAX_USERNAME_LEN) == 0) {
                                /* Users cannot like their own message */
                                isLikedAlready = TRUE;
                            } else {
                                isLikedAlready = isLikedByUser(userMsg->userName, chatMsgNodePtr);
                            }

                            if(!isLikedAlready) {
                                /* Handle the Like Message - Create a LIKE file and send the LIKE message to the client Group. */
                                constructLTS(&(serverMsg->lts));
                                writeLikeUnlikeMessageToFile(serverMsg);

                                /* Add it to it's data structure. */
                                handleUserMsg_Like_And_Unlike(serverMsg, chatMsgNodePtr);

                                /* Notify Clients */
                                constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                                printf("\nSending message to Client group [%s] \n", gChatRoom);
                                ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                                if (ret < 0) {
                                    SP_error(ret);
                                    Bye();
                                }

                                /* Notify Servers */
                                printf("\nSending message to Servers group [%s] \n", SERVERS_GROUP);
                                ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER, sizeof(ServerMsg), (char *)serverMsg);
                                if (ret < 0) {
                                    SP_error(ret);
                                    Bye();
                                }
                            }

                            break;
                            
                        case USERMSG_UNLIKE:
                            /* Checks if the user has already liked the message.  */
                            chatMsgNodePtr = findChatMsg( ((LTS*)(userMsg->msg))->serverId, ((LTS*)(userMsg->msg))->localId );
                            if(strncmp(chatMsgNodePtr->msgDetails.userMsg.userName, userMsg->userName, MAX_USERNAME_LEN) == 0) {
                                /* Users cannot unlike their own message */
                                isLikedAlready = FALSE;
                            } else {
                                isLikedAlready = isLikedByUser(userMsg->userName, chatMsgNodePtr);
                            }

                            if(isLikedAlready) {
                                /* Handle the Unlike Message - Create a UNLIKE file and send the UNLIKE message to the client Group. */
                                constructLTS(&(serverMsg->lts));
                                writeLikeUnlikeMessageToFile(serverMsg);

                                /* Add it to it's data structure. */
                                handleUserMsg_Like_And_Unlike(serverMsg, chatMsgNodePtr);

                                /* Notify Clients */
                                constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                                printf("\nSending message to Client group [%s] \n", gChatRoom);
                                ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                                if (ret < 0) {
                                    SP_error(ret);
                                    Bye();
                                }

                                /* Notify Servers */
                                printf("\nSending message to Servers group [%s] \n", SERVERS_GROUP);
                                ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER, sizeof(ServerMsg), (char *)serverMsg);
                                if (ret < 0) {
                                    SP_error(ret);
                                    Bye();
                                }

                            }
                            break;

                        case USERMSG_HISTORY:
                            /* Send to Client: All chat room msgs & list of users in the chat room to the user who requested. */
                            constructClientMsg(userMsg->chatRoom, serverSendbuf, TRUE);
                            tmpClientMsgHdr = (ClientMsgHdr*)serverSendbuf;
                            msgLength = sizeof(ClientMsgHdr) + (tmpClientMsgHdr->numUsers)*MAX_USERNAME_LEN + (tmpClientMsgHdr->numOfChatMsgs)*sizeof(ChatMsgInfo);
                            ret = SP_multicast(Mbox, FIFO_MESS, sender, SERVER_TO_CLIENT, msgLength, serverSendbuf);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            break;

                        case USERMSG_VIEW:
                            data = serverSendbuf;
                            for(loopCtr = 0; loopCtr < NUM_SERVERS; loopCtr++) {
                                ((ServerInfo*)data)->serverId = loopCtr;
                                if(msgsHead[loopCtr] != NULL) {
                                    memcpy( &(((ServerInfo*)data)->highestLts), &(msgsHead[loopCtr]->msgDetails.lts), sizeof(LTS) );
                                } else {
                                    ((ServerInfo*)data)->highestLts.serverId = loopCtr;
                                    ((ServerInfo*)data)->highestLts.globalId = -1;
                                    ((ServerInfo*)data)->highestLts.localId = -1;
                                }
                                data = data + sizeof(ServerInfo);
                            }
                            ret = SP_multicast(Mbox, FIFO_MESS, sender, SERVER_TO_CLIENT_DEBUG, NUM_SERVERS * sizeof (ServerInfo), serverSendbuf);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            break;
                    }
                }

                /**********************  2. From a Server  **********************/
                else if (mess_type == SERVER_TO_SERVER && strcmp(target_groups[0], SERVERS_GROUP) == 0) {
                    serverMsg = (ServerMsg*) serverSendbuf;
                    memcpy(serverSendbuf, recv_buf, sizeof (ServerMsg));  /* Use separate buffers for sending and recving */

                    printf("\nRecieved Server Message ... \n");
                    printServerMsg((ServerMsg*)recv_buf);
                    /* Switch case for: Join, Leave, Append, Like, Unlike */

                    switch(serverMsg->userMsg.type) {

                        case USERMSG_JOIN:
                            /* Add user to the list of current users in that chatroom. Here the clientId used does not matter. */
                            addUserToChatRoom(serverMsg->lts.serverId, serverMsg->userMsg.chatRoom, serverMsg->userMsg.userName, sender, NULL);

                            /* Notify Clients */
                            constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                            printf("\nSending message to Client group [%s] \n", gChatRoom);
                            ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            break;

                        case USERMSG_LEAVE:
                            /* Remove this user from the list of current users */
                            removeUserFromChatRoom(serverMsg->lts.serverId, serverMsg->userMsg.chatRoom, serverMsg->userMsg.userName, serverMsg->clientId);

                            /* Notify Clients */
                            constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                            printf("\nSending message to Client group [%s] \n", gChatRoom);
                            ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                            if (ret < 0) {
                                SP_error(ret);
                                Bye();
                            }
                            break;

                        case USERMSG_APPEND:
                            if(!isIgnore(&serverMsg->lts)) {
                                updateGlobalId(serverMsg->lts.globalId);
                                writeAppendMsgToFile(serverMsg);

                                handleUserMsg_Append(serverMsg);

/*
                                printf("WHY DNT YOU SEND [%d]\n", isCurrentlyHealing);
*/

                                if(!isCurrentlyHealing) {
                                    /* Notify Clients */
                                    constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                                    printf("\nSending message to Client group [%s] \n", gChatRoom);
                                    ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                                    if (ret < 0) {
                                        SP_error(ret);
                                        Bye();
                                    }
                                    printf("Sending user message to client group [%s]\n", gChatRoom);
                                }
                            } else {
                                printf("Ignoring message as Head LTS is - ");
                                printLTS(&(msgsHead[serverMsg->lts.serverId]->msgDetails.lts));
                                printf("\n");
                            }
                            break;

                        case USERMSG_LIKE:
                            /* Do not check anything here. Because respect what server sends. */
                            if(!isIgnore(&serverMsg->lts)) {
                                updateGlobalId(serverMsg->lts.globalId);
                                writeLikeUnlikeMessageToFile(serverMsg);

                                /* Add it to it's data structure. */
                                handleUserMsg_Like_And_Unlike(serverMsg, NULL);
                                if(!isCurrentlyHealing) {
                                    /* Notify Clients */
                                    constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                                    printf("\nSending message to Client group [%s] \n", gChatRoom);
                                    ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                                    if (ret < 0) {
                                        SP_error(ret);
                                        Bye();
                                    }
                                }
                            } else {
                                printf("Ignoring message as Head LTS is - ");
                                printLTS(&(msgsHead[serverMsg->lts.serverId]->msgDetails.lts));
                                printf("\n");
                            }
                            break;

                        case USERMSG_UNLIKE:
                            /* Do not check anything here. Because respect what server sends. */
                            if(!isIgnore(&serverMsg->lts)) {
                                updateGlobalId(serverMsg->lts.globalId);
                                writeLikeUnlikeMessageToFile(serverMsg);

                                /* Add it to it's data structure. */
                                handleUserMsg_Like_And_Unlike(serverMsg, NULL);

                                if(!isCurrentlyHealing) {
                                    /* Notify Clients */
                                    constructChatRoomGroupName(serverMsg->userMsg.chatRoom, gChatRoom, myServerId);
                                    printf("\nSending message to Client group [%s] \n", gChatRoom);
                                    ret = SP_multicast(Mbox, AGREED_MESS, gChatRoom, SERVER_TO_CLIENT_GROUP, sizeof(ServerMsg), (char *)serverMsg);
                                    if (ret < 0) {
                                        SP_error(ret);
                                        Bye();
                                    }
                                }
                            } else {
                                printf("Ignoring message as Head LTS is - ");
                                printLTS(&(msgsHead[serverMsg->lts.serverId]->msgDetails.lts));
                                printf("\n");
                            }
                            break;

                    }


                    /* Check if all required is recvd, if so, go back to normal state */
                    if(isCurrentlyHealing == TRUE) {
                        for(i = 0; i < (NUM_SERVERS); i++) {
                            if(msgsHead[i]!=NULL 
                                    && msgsHead[i]->msgDetails.lts.localId >= getMax(i)) {
                                continue;
                            } else if(msgsHead[i]== NULL && getMax(i) == -1) {
                                continue;
                            } else {
                                break;
                            }
                        }
                        if(i == NUM_SERVERS) {
                            notifyAllClients(serverSendbuf);
                            printf("**** setting isCurrentlyHealing TO FALSE **** \n");
                            isCurrentlyHealing = FALSE;
                        }
                    }
                }

                /**********************  3. Partition or Merge  **********************/
                else if ((mess_type == SERVER_TO_SERVER_P) && (strcmp(target_groups[0], SERVERS_GROUP) == 0)) {

                    printf("Recieved Partition Message...\n");
                    printPartitionMsg(recv_buf);

                    /* 3. Recv LTS vectors from Others */
                    parsePartitionMsg(recv_buf);

                    isContinue = areAllPartionMsgsRecvd();

                    if(isContinue) {

                        printf("All Partition Messages Recieved \n");

                        /* If I have nothing to recieve from others, right away send update to clients */
                        for(i = 0; i < (NUM_SERVERS); i++) {
                            if(localIndex[myServerId][i] == getMax(i)) {
                                continue;
                            } else {
                                break;
                            }
                        }
                        if(i == NUM_SERVERS) {
                            /* Just send update to all client groups */
                            notifyAllClients(serverSendbuf);
                            printf("**** I have nothing to recieve. Setting isCurrentlyHealing TO FALSE **** \n");
                            isCurrentlyHealing = FALSE;
                        }
                        

                        /* 4. Run Algo to check what all to send */
                        for(i = 0; i < (NUM_SERVERS); i++) {
                            /* Among the servers who are not there, determine, for whose msgs I am responsible */
                            if(currentServers[i] == FALSE) {
                                /* Determine if I should send his msgs */
                                if(localIndex[myServerId][i] == getMax(i)) {
                                    serversResponsibleFor[i] = TRUE;
                                    numServersResponsibleFor++;
                                    lastSentLocalIdVector[i] = getMin(i);
                                } else {
                                    serversResponsibleFor[i] = FALSE;
                                }
                            }
                        }
                        /* I am responsible for myself */
                        lastSentLocalIdVector[myServerId] = getMin(myServerId);
                        serversResponsibleFor[myServerId] = TRUE;

                        /* 5. Start Sending Msgs */
                        for(i = 0; i < (NUM_SERVERS); i++) {
                            if(serversResponsibleFor[i] == TRUE) {
                                /* Start Sending msgs */
                                printf("For server [%d], Finding msg with localId = [%d]\n", i, lastSentLocalIdVector[i]+1);
                                chatMsgNodePtr = findChatMsg(i, lastSentLocalIdVector[i]+1);
                                while(chatMsgNodePtr != NULL) {
                                    /* Notify Servers */
                                    printf("\nHealing: Sending message to Server group [%s] \n", SERVERS_GROUP);
                                    printServerMsg(&chatMsgNodePtr->msgDetails);
                                    ret = SP_multicast(Mbox, FIFO_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER,
                                                                    sizeof(ServerMsg), (char *)(&chatMsgNodePtr->msgDetails));
                                    if (ret < 0) {
                                        SP_error(ret);
                                        Bye();
                                    }
                                    lastSentLocalIdVector[i] = chatMsgNodePtr->msgDetails.lts.localId;
                                    chatMsgNodePtr = chatMsgNodePtr->prev;
                                }
                            }
                        }

                    }

                }

	}
        else if( Is_membership_mess( service_type ) )
        {
                ret = SP_get_memb_info( mess, service_type, &memb_info );
                if (ret < 0) {
                        printf("BUG: membership message does not have valid body\n");
                        SP_error( ret );
                        exit( 1 );
                }
		if     ( Is_reg_memb_mess( service_type ) )
		{
			printf("Received REGULAR membership for group %s with %d members, where I am member %d:\n",
				sender, num_groups, mess_type );
			for( i=0; i < num_groups; i++ )
				printf("\t%s\n", &target_groups[i][0] );
			printf("grp id is %d %d %d\n",memb_info.gid.id[0], memb_info.gid.id[1], memb_info.gid.id[2] );

			if( Is_caused_join_mess( service_type ) )
			{
				printf("Due to the JOIN of %s\n", memb_info.changed_member );
			}else if( Is_caused_leave_mess( service_type ) ){
				printf("Due to the LEAVE of %s\n", memb_info.changed_member );
			}else if( Is_caused_disconnect_mess( service_type ) ){
				printf("Due to the DISCONNECT of %s\n", memb_info.changed_member );
			}else if( Is_caused_network_mess( service_type ) ){
				printf("Due to NETWORK change with %u VS sets\n", memb_info.num_vs_sets);
                                num_vs_sets = SP_get_vs_sets_info( mess, &vssets[0], MAX_VSSETS, &my_vsset_index );
                                if (num_vs_sets < 0) {
                                        printf("BUG: membership message has more then %d vs sets. Recompile with larger MAX_VSSETS\n", MAX_VSSETS);
                                        SP_error( num_vs_sets );
                                        exit( 1 );
                                }
                                for( i = 0; i < num_vs_sets; i++ )
                                {
                                        printf("%s VS set %d has %u members:\n",
                                               (i  == my_vsset_index) ?
                                               ("LOCAL") : ("OTHER"), i, vssets[i].num_members );
                                        ret = SP_get_vs_set_members(mess, &vssets[i], members, MAX_MEMBERS);
                                        if (ret < 0) {
                                                printf("VS Set has more then %d members. Recompile with larger MAX_MEMBERS\n", MAX_MEMBERS);
                                                SP_error( ret );
                                                exit( 1 );
                                        }
                                        for( j = 0; j < vssets[i].num_members; j++ )
                                                printf("\t%s\n", members[j] );
                                }
			}

                        if((strcmp(sender, SERVERS_GROUP) == 0)) {
                            /********** Partition and healing ***********/

                            /* 1. Reset all varaibles */
                            resetAllPartitioningVars(num_groups);

                            if(num_groups == 1) {
                                /* When only I am there in the group */
                                notifyAllClients(serverSendbuf);
                                printf("**** I am alone. Setting isCurrentlyHealing TO FALSE **** \n");
                                isCurrentlyHealing = FALSE;
                            } else {
                                msgLength = constructPartitionMsg(serverSendbuf);

                                printf("Sending Partition Message...\n");
                                printPartitionMsg(serverSendbuf);

                                /* 2. Send this to all */
                                ret = SP_multicast(Mbox, AGREED_MESS | SELF_DISCARD, SERVERS_GROUP, SERVER_TO_SERVER_P, msgLength, serverSendbuf);
                                if (ret < 0) {
                                    SP_error(ret);
                                    Bye();
                                }
                            }
                        }

		}else if( Is_transition_mess(   service_type ) ) {
			printf("received TRANSITIONAL membership for group %s\n", sender );
		}else if( Is_caused_leave_mess( service_type ) ){
			printf("received membership message that left group %s\n", sender );
		}else printf("received incorrecty membership message of type 0x%x\n", service_type );
        } else if ( Is_reject_mess( service_type ) )
        {
		printf("REJECTED message from %s, of servicetype 0x%x messtype %d, (endian %d) to %d groups \n(%d bytes): %s\n",
			sender, service_type, mess_type, endian_mismatch, num_groups, ret, mess );
	}else printf("received message of unknown message type 0x%x with ret %d\n", service_type, ret);

	printf("\n");
	fflush(stdout);
}


static  void	Bye()
{
	To_exit = 1;

	printf("\nBye.\n");

	SP_disconnect( Mbox );

	exit( 0 );
}
